package com.jeeplus.netty;


import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.support.spring.FastJsonContainer;
import com.jeeplus.modules.sys.utils.LogUtils;
import com.jeeplus.netty.net.Base64;
import com.jeeplus.netty.proto.User;
import com.jeeplus.netty.proto.YanruTestProtoBuf;
import com.jeeplus.utils.ByteUtils;
import com.jeeplus.utils.ProtoBufUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;
import org.hibernate.hql.spi.id.cte.CteValuesListBuilder;
import org.omg.CORBA.Current;
import org.omg.IOP.Encoding;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.objenesis.Objenesis;
import org.springframework.objenesis.ObjenesisStd;

import io.protostuff.LinkedBuffer;
import io.protostuff.ProtostuffIOUtil;
import io.protostuff.Schema;
import io.protostuff.runtime.RuntimeSchema;

//接收并处理来自客户端的消息
/**
 * @Description: 处理消息的handler
 * TextWebSocketFrame： 在netty中，是用于为websocket专门处理文本的对象，frame是消息的载体SimpleChannelHandler
 */
//public class ChatHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {extends SimpleChannelInboundHandler<HttpObject>
public class ChatHandler extends SimpleChannelInboundHandler<Object> {

    // 用于记录和管理所有客户端的channle
    public static ChannelGroup users =
            new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);


    //yanruTODO 怎么保证，static 是线程安全的
    //缓存用户
    public static List<ChannelHandlerContext> userList = new ArrayList<ChannelHandlerContext>();

    protected Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * 当客户端连接服务端之后（打开连接）
     * 获取客户端的channle，并且放到ChannelGroup中去进行管理
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        writeFile("客户端连接信息  remoteAddress = " + ctx.channel().remoteAddress() + "  ,id = " + ctx.channel().id().asShortText());
        users.add(ctx.channel());


        //添加到channelGroup 通道组
        MyChannelHandlerPool.channelGroup.add(ctx.channel());
        userList.add(ctx);
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {

        String channelId = ctx.channel().id().asShortText();
        writeFile("客户端被移除，channelId为：" + channelId);

        removeClient(ctx);
    }

    //移除客户端
    private void removeClient(ChannelHandlerContext ctx)
    {
        String channelId = ctx.channel().id().asShortText();
        logger.info("移除本地客户端数据 channelId:"+channelId);
        // 当触发handlerRemoved，ChannelGroup会自动移除对应客户端的channel
        users.remove(ctx.channel());
        userList.remove(ctx);


        // 从channelGroup中移除，当有客户端退出后，移除channel。
        MyChannelHandlerPool.channelGroup.remove(ctx.channel());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        // 发生异常之后关闭连接（关闭channel），随后从ChannelGroup中移除
        ctx.channel().close();

        // 从channelGroup中移除，当有客户端退出后，移除channel。
        MyChannelHandlerPool.channelGroup.remove(ctx.channel());

        logger.debug("exceptionCaught通道发生了异常信息，具体信息如下："+cause.getMessage());
//        cause.printStackTrace();
        //users.remove(ctx.channel());
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
        super.channelReadComplete(ctx);
    }


    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, Object msg) throws Exception {

        if(msg instanceof String){

         //    logger.info("收到的字符串str="+msg);

            //这个是对的！
            byte [] dataBytes = msg.toString().getBytes();//yanruTODO 先不用utf8  .getBytes("UTF-8");

            //writeFile("收到消息长度 >>>>>>>>>>>>>  " + dataBytes.length );
//            writeFile("收到消息 >>>>>>>>>>>>>  " + msg );

            //将收到的消息，用分隔符，将 协议名称 和数据分开
            List<Byte> byteList = new ArrayList<Byte>(); //数据包部分
            String protoName = ""; //协议名部分
            writeFilePath("D:\\yanrulog\\server_接收_原始数据_"+new Date().getTime()+".txt", getBytesString(dataBytes));


            //yanruTODO 将元数据转为int[] 数组看看
            int [] array1 = new int[dataBytes.length];
            String s = "";
            for(int index =0; index < dataBytes.length; index ++)
            {
                int num1=dataBytes[index]&0xff;
                array1[index] = num1;
                s += dataBytes[index]  +"\n";
            }
            writeFilePath("D:\\yanrulog\\server_接收客户端_原始Bytes数组_"+new Date().getTime()+".txt", s);

            //名字部分 测试
            List<Byte> nameByteList = new ArrayList<Byte>();

            int nameLength = 20;// 200;
            byte[] byteName = subByte(dataBytes, 0, nameLength);
            String protoName1 = new String(byteName);
            protoName1 = protoName1.trim();
            //yanruTODO 需要剔除空的字符
            logger.info("收到 协议名称1="+protoName1+",byteName.length="+byteName.length);

            byte[] dataBodyBytes = subByte(dataBytes, nameLength, dataBytes.length-nameLength);

            logger.info("收到 数据包长度dataBodyBytes="+dataBodyBytes.length);

            //yanruTODO
//            for(int byteIndex = 0; byteIndex < dataBytes.length; byteIndex++)
//            {
//                if (dataBytes[byteIndex] == 0) //表示遇到分隔符
//                {
//                    //前面部分是协议名称
//                    protoName = new String(ByteUtils.convertListArray(byteList));
//                    nameByteList.addAll(byteList);
//
//                    byteList.clear();
//                }
//                else
//                {
//                    byteList.add(dataBytes[byteIndex]);
//                }
//            }
//            logger.info("服务器接收到的协议名是="+protoName+",协议名长度="+nameByteList.size()+",正文数据包长度="+byteList.size()+",总长度="+dataBytes.length);
//
//            //用byte 不够接C#的 byte
//            byte [] dataBodyBytes = ByteUtils.convertListArray(byteList);
//            String path =  "D:\\server_get_bytes_" + new Date().getTime() +  ".txt";

             writeFilePath("D:\\yanrulog\\服务器接收数据包长度_"+new Date().getTime()+".txt", getBytesString(dataBodyBytes));

             //2020-5-14 将收到的消息 反序列化，看看是什么消息
            //yanruTODO 这里暂时写死了 byteList.toArray(new Byte[byteList.size()])
            YanruTestProtoBuf testBuff = null;
            try {
                  testBuff = ProtoBufUtil.deserializer(dataBodyBytes, YanruTestProtoBuf.class);

                logger.info("此时收到的数据包是protoName="+testBuff.protoName);
                logger.info("此时收到的数据包是getString="+testBuff.getString());
            }
            catch (Exception ex)
            {
                testBuff = new YanruTestProtoBuf();
                testBuff.protoName =  protoName1;
                testBuff.result = -10086;
                logger.info("反序列化结果报错ex="+ex);
            }

                //修改数据包
                //      testBuff
            //yanruTODO 暂时不动
                //testBuff.stringName = "服务器回应修改";
             //   testBuff.intNumber = 6;
               // testBuff.childBuff.testInt = 120;

                //回应的数据包部分
                String responseProtoName = testBuff.protoName;

               logger.info("此时收到的数据包是 responseProtoName="+responseProtoName);

                //为什么序列化完 有那么多的0
                byte[] responseBodyBytes = ProtoBufUtil.serializer(testBuff);
                writeFilePath("D:\\yanrulog\\服务器数据段序列化之后_"+new Date().getTime()+".txt", getBytesString(responseBodyBytes));


                //yanruTODO 待拼装新的包 格式： 协议名+ 空字符+ 包体数据+ 结束符字节数组

                //2020-5-14 重新修饰，返回的数据
         //       byte [] bytes1 = dataBytes;//yanruTODO 原文 返回  (respondMsg).getBytes("UTF-8");


                byte[] protoNameArray = responseProtoName.getBytes("UTF-8"); //协议名称
                int headSize = 20;
                byte[] bytesHead = new byte[headSize];// byteMerger(protoNameArray, NettyConst.dataSplit);//追加 分割
                for(int index = 0; index < protoNameArray.length; index ++)
                {
                    bytesHead[index] = protoNameArray[index];
                }
                byte[] byteTotalBody = byteMerger(bytesHead, responseBodyBytes);// 追加数据


                //作为报文头的数据长度
                byte [] dataLength = toLH(byteTotalBody.length);// new byte[4];//用四个字节告诉客户端，这是数据的长度;

                //将数据段 + 结尾分割数组
                byte [] sendMsgTotal =  byteMerger(dataLength, byteTotalBody);//, NettyConst.dataEndingFlag);



                writeFilePath("D:\\yanrulog\\服务器_"+" 0 协议单纯名字 "+new Date().getTime()+".txt", getBytesString(protoNameArray));
                writeFilePath("D:\\yanrulog\\服务器_"+" 1 协议冗余名称 "+new Date().getTime()+".txt", getBytesString(bytesHead));
                writeFilePath("D:\\yanrulog\\服务器_"+" 2 仅仅数据段 "+new Date().getTime()+".txt", getBytesString(responseBodyBytes));
                writeFilePath("D:\\yanrulog\\服务器_"+" 3 叠加数据段 "+new Date().getTime()+".txt", getBytesString(byteTotalBody));
                writeFilePath("D:\\yanrulog\\服务器_"+" 4 包含结束"+new Date().getTime()+".txt", getBytesString(sendMsgTotal));
                writeFilePath("D:\\yanrulog\\服务器_"+" 5 包头数据长度 "+new Date().getTime()+".txt", getBytesString(dataLength));

//                writeFilePath("D:\\yanrulog\\服务器_"+""+new Date().getTime()+".txt", getBytesString(byteTotalBody));
//                writeFilePath("D:\\yanrulog\\服务器_"+""+new Date().getTime()+".txt", getBytesString(sendMsgTotal));

             //   writeFilePath("D:\\yanrulog\\服务器_回复段4_"+new Date().getTime()+".txt", getBytesString(sendMsgTotal));


                //给本用户回复
                sendChannelMsg(channelHandlerContext, sendMsgTotal);

                //这个会导致客户端收不到数据
                //群发给其他人
                //收到信息后，群发给所有小伙伴
                logger.info("给大家回复时MyChannelHandlerPool.channelGroup.size()="+userList.size());

                ByteBuf resp = Unpooled.copiedBuffer(sendMsgTotal);
                for(int index = 0; index < userList.size(); index ++)
                {
                    logger.info("给单独用户回复长度="+sendMsgTotal.length);
                    userList.get(index).writeAndFlush(resp);//yanruTODO 暂时原文发送
                }

//            }
//            catch (Exception ex)
//            {
//                logger.info("反序列化出现报错 此时ex="+ex.getMessage());
//                //传来的二进制数组有问题，表明，有可能这是一个有问题的数据包，
//                if(ex != null && "Reading from a byte array threw an IOException (should never happen).".equals(ex.getMessage()))
//                {
//                    //yanruTODO 该条件尚未走到测试
//                    channelHandlerContext.close(); //关闭客户端， 此时会调用 handlerRemoved
//                    removeClient(channelHandlerContext);
//                }
//            }


            //yanruTODO 测试对象
            /**
             //测试代码
             User user = new User();
             user.setUserId(1);
             user.setUserTypeId(1);
             user.setUserName("XRQ");
             user.setCreateDateTime(new Date());
             //序列化成ProtoBuf数据结构
             byte[] userProtoObj= ProtoBufUtil.serializer(user);

             //ProtoBuf数据结构反序列化成User对象
             User newUserObj = ProtoBufUtil.deserializer(userProtoObj, User.class);

             *
             */


        }
        else
        {
            writeFile("异常 收到msg="+msg);
        }

    }

    public static byte[] toLH(int n) {
        byte[] b = new byte[4];
        b[0] = (byte) (n & 0xff);
        b[1] = (byte) (n >> 8 & 0xff);
        b[2] = (byte) (n >> 16 & 0xff);
        b[3] = (byte) (n >> 24 & 0xff);
        return b;
    }

    //截取数组
    public byte[] subByte(byte[] b,int off,int length){
        byte[] b1 = new byte[length];
        System.arraycopy(b, off, b1, 0, length);
        return b1;
    }

    /**
     * 对象转数组
     * @param obj
     * @return
     */
    public byte[] toByteArray (Object obj) {
        byte[] bytes = null;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(obj);
            oos.flush();
            bytes = bos.toByteArray ();
            oos.close();
            bos.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        return bytes;
    }

    //拼接两个byte数组
    public static byte[] byteMerger(byte[] byte_1, byte[] byte_2){
        byte[] byte_3 = new byte[byte_1.length+byte_2.length];
        System.arraycopy(byte_1, 0, byte_3, 0, byte_1.length);
        System.arraycopy(byte_2, 0, byte_3, byte_1.length, byte_2.length);
        return byte_3;
    }


    public void sendChannelMsg(ChannelHandlerContext channelHandlerContext, byte[] sendMsg) throws IOException {
        //byte [] bytes = StringCoding.encode(value, 0, value.length);;
        ByteBuf resp = Unpooled.copiedBuffer(sendMsg);
        writeFile("服务器回应长度bytes.length="+sendMsg.length+",sendMsg.length="+sendMsg.length);
        channelHandlerContext.write(resp); //表示立即发送给客户端
    }


    //测试写文件
    private String fileName = "D:\\yanru_output_6.txt";

    public  String getBytesString(byte [] bytes)
    {
        StringBuilder builder = new StringBuilder();
        for(int index = 0; index < bytes.length; index ++)
        {
     //       logger.info("元素["+index+"]="+bytes[index]);
            builder.append(bytes[index] + "\n");
        }
    //    writeFilePath(path, builder.toString());
        return builder.toString();
    }

    //写入额外文件
    public  void writeFilePath(String path, String str)
    {
   //     String path = p;// "D:\\"+p+"_" + new Date().getTime() +  ".txt";
        try {
            File writeName = new File(path); // 相对路径，如果没有则要建立一个新的output.txt文件
            if(!writeName.exists())
            {
                writeName.createNewFile(); // 创建新文件,有同名的文件的话直接覆盖
            }
            try
            {
                FileOutputStream fos = new FileOutputStream(writeName);
                // 把数据写入到输出流
                fos.write((str+"\r\n").getBytes("UTF-8"));
                // 关闭输出流
                fos.close();
            }
            catch (Exception e)
            {

            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 写入TXT文件
     */
    public  void writeFile(String str) throws IOException {
        logger.debug(str);
        try {
            File writeName = new File(fileName); // 相对路径，如果没有则要建立一个新的output.txt文件
            if(!writeName.exists())
            {
                writeName.createNewFile(); // 创建新文件,有同名的文件的话直接覆盖
            }
            try
            {
                FileOutputStream fos = new FileOutputStream(writeName);
                // 把数据写入到输出流
                fos.write((str+"\r\n").getBytes("UTF-8"));
                // 关闭输出流
                fos.close();
            }
            catch (Exception e)
            {

            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //将对象转为 byte[]
    public static byte[] ObjectToByte(java.lang.Object obj) {
        byte[] bytes = null;
        try {
            // object to bytearray
            ByteArrayOutputStream bo = new ByteArrayOutputStream();
            ObjectOutputStream oo = new ObjectOutputStream(bo);
            oo.writeObject(obj);

            bytes = bo.toByteArray();

            bo.close();
            oo.close();
        } catch (Exception e) {
            System.out.println("translation" + e.getMessage());
            e.printStackTrace();
        }
        return bytes;
    }
}
